#include "linux-uart.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

LinuxUart::LinuxUart(const string &deviceName, int baudRate)
{
	fd = open(deviceName.c_str(), O_RDWR | O_NOCTTY);
	if(fd < 0)
	{
		fprintf(stderr, "Fail to open %s,err:%s\n", deviceName.c_str(), strerror(errno));
		exit(EXIT_FAILURE);
	}

	printf("open uart:%s success\n", deviceName.c_str());
	bool ok = defaultInit(baudRate);
	if(!ok)
	{
		fprintf(stderr, "Fail to default init Uart\n");
		exit(EXIT_FAILURE);
	}
}

LinuxUart::~LinuxUart()
{
	close(fd);
}

bool LinuxUart::defaultInit(int baudRate)
{
	int            ret;
	int            err;
	struct termios tio;

	bzero(&tio, sizeof(tio));
	tio.c_cflag |= CLOCAL | CREAD;
	tio.c_cflag &= ~CSIZE;

	// 设置数据位为8bit
	tio.c_cflag |= CS8;

	// 设置校验位:无奇偶校验位
	tio.c_cflag &= ~PARENB;

	// 设置波特率
	switch(baudRate)
	{
	case 4800:
		cfsetispeed(&tio, B4800);
		cfsetospeed(&tio, B4800);
		break;
	case 9600:
		cfsetispeed(&tio, B9600);
		cfsetospeed(&tio, B9600);
		break;
	case 57600:
		cfsetispeed(&tio, B57600);
		cfsetospeed(&tio, B57600);
		break;
	case 115200:
		cfsetispeed(&tio, B115200);
		cfsetospeed(&tio, B115200);
		break;
	default:
		fprintf(stderr, "The baudrate:%d is not support\n", baudRate);
		return false;
	}

	// 设置停止位为1bit
	tio.c_cflag &= ~CSTOPB;

	// 设置等待时间和最小接收字符
	tio.c_cc[VTIME] = 80;
	tio.c_cc[VMIN]  = 0;

	// 刷新串口:处理未接收字符
	tcflush(fd, TCIOFLUSH);

	// 设置参数
	err = tcsetattr(fd, TCSANOW, &tio);
	if(err)
	{
		fprintf(stderr, "Fail to tcsetattr,err:%s\n", strerror(errno));
		return false;
	}

	return true;
}

int LinuxUart::readData(uint8_t *buf, uint32_t size)
{
	int n;

	n = read(fd, buf, size);
	if(n < 0)
	{
		fprintf(stderr, "Fail to read,err:%s\n", strerror(errno));
		return -1;
	}

	return n;
}

int LinuxUart::writeData(const uint8_t *buf, uint32_t size)
{
	int n;

	n = write(fd, buf, size);
	if(n < 0)
	{
		fprintf(stderr, "Fail to write,err:%s\n", strerror(errno));
		return -1;
	}

	return n;
}

int LinuxUart::readFixLenData(uint8_t *buf, uint32_t fixLen)
{
	int n;
	int count = 0;
	while(count < fixLen)
	{
		n = read(fd, buf + count, fixLen - count);
		if(n <= 0)
		{
			break;
		}
		count += n;
	}

	return count != fixLen ? -1 : fixLen;
}
